/*
 * Copyright (C) 2020  即时通讯网(52im.net) & Jack Jiang.
 * The MobileIMSDK v5.x Project.
 * All rights reserved.
 *

 *
 * "即时通讯网(52im.net) - 即时通讯开发者社区!" 推荐开源工程。
 *
 * OnlineProcessor.java at 2020-8-22 16:00:59, code by Jack Jiang.
 */
package com.ruoyi.web.netty;

import io.netty.channel.Channel;
import io.netty.util.AttributeKey;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * MibileIMSDK的服务端用户列表管理类。
 * <p>
 * 根据全局算法约定，当user_id=0时即表示是服务器。
 *

 * @version 3.1
 */
public class OnlineProcessor
{
	/** 用于用户session中存放user_id的属性key */
	public final static String USER_ID_IN_SESSION_ATTRIBUTE = "__user_id__";

	public static final AttributeKey<String> USER_ID_IN_SESSION_ATTRIBUTE_ATTR =
			AttributeKey.newInstance(USER_ID_IN_SESSION_ATTRIBUTE);

	public static boolean DEBUG = false;

	private static Logger logger = LoggerFactory.getLogger(OnlineProcessor.class);
	private static OnlineProcessor instance = null;

	/** 用户在线列表：key=user_id、value=会话实例引用 */
	private ConcurrentMap<String, Channel> onlineSessions = new ConcurrentHashMap<String, Channel>();

	/**
	 * 为了简化API调用，本方法将以单例的形式存活。
	 *
	 * @return 本类的全局单例
	 */
	public static OnlineProcessor getInstance()
	{
		if(instance == null)
			instance = new OnlineProcessor();
		return instance;
	}

	private OnlineProcessor()
	{
	}


	public void putUser(String userId, Channel session)
	{
//		if (loginInfo.getExtra() == null){
//			loginInfo.setExtra("");
//		}

		if(onlineSessions.containsKey(userId))
		{
			System.out.println("================================================");
			logger.debug("[IMCORE-{}]【注意】用户id={}已经在在线列表中了，session也是同一个吗？{}");



			// TODO 同一账号的重复登陆情况可在此展开处理逻辑
		}

		// 将用户加入到在线列表中
//		onlineSessions.put(loginInfo.getLoginUserId(), session);
		onlineSessions.put(userId, session);

		__printOnline();// just for debug
	}

	/**
	 * 打印在线用户列。
	 * <p>
	 * 本方法仅应用于DEBUG时，当在线用户数量众多时，本方法会影响性能。
	 */
	public void __printOnline()
	{
		logger.debug("【@】当前在线用户共("+onlineSessions.size()+")人------------------->");
		if(DEBUG)
		{
			for(String key : onlineSessions.keySet())
				logger.debug("      > user_id="+key+",session="+onlineSessions.get(key).remoteAddress());
		}
	}

	/**
	 * 将用户从在线列表中移除.
	 * <p>
	 * <b><font color="#ff0000">本方法由MobileIMSDK内部决定如
	 * 何调用，不建议开发者调用此方法！</font></b>
	 *
	 * @param user_id 用户的user_id
	 * @return true表示已成功remove，否则表示没有此user_id对应的在线信息
	 */
	public boolean removeUser(String user_id)
	{
		synchronized(onlineSessions)
		{
			if(!onlineSessions.containsKey(user_id))
			{
				logger.warn("[IMCORE]！用户id={}不存在在线列表中，本次removeUser没有继续.", user_id);
				__printOnline();// just for debug
				return false;
			}
			else
				return (onlineSessions.remove(user_id) != null);
		}
	}

	/**
	 * 根据user_id获得该在线用户对应的 Netty UDP Channel 会话实例句柄。
	 *
	 * @param user_id 用户的user_id
	 * @return 存在该在线用户则成功返回，否则返回null
	 */
	public Channel getOnlineSession(String user_id)
	{
//		logger.debug("======>user_id="+user_id+"在列表中吗？"+usersBySession.containsKey(user_id));
//		__printOnline();

		if(user_id == null)
		{
			logger.warn("[IMCORE][CAUTION] getOnlineSession时，作为key的user_id== null.");
			return null;
		}

		return onlineSessions.get(user_id);
	}

	/**
	 * 返回用户在线列表：key=user_id、value=会话实例引用。
	 *
	 * @return 在线列表实例引用
	 */
	public ConcurrentMap<String, Channel> getOnlineSessions()
	{
		return onlineSessions;
	}

	//------------------------------------------------------------------ 实用方法
	/**
	 * 该用户会话是否是合法的（已登陆认证）。
	 * <p>
	 * 根据MINA的原理，任何请求都会建立会话，但会话是否是合法的，则需根据
	 * 存放于其会话中的登陆属性来校验（MobileIMSDK中，已经过登陆认证的会话
	 * ，会在其session中存放user_id，判断是否已设置user_id即可认定是否是
	 * 合法的会话）。
	 *
	 * @param session 用户会话引用
	 * @return true表示已经成功登陆认证过，否则表示未登陆过（非法请求）
	 */
	public static boolean isLogined(Channel session)
	{
		return session != null && getUserIdFromSession(session) != null;
	}

	/**
	 * 尝试取出存放于用户会话中的user_id.通常只有已成功登陆验证后的用户
	 * 会话中才会存放它对应的user_id.
	 *
	 * @param session 用户会话引用
	 * @return 如果找到该属性则返回指定session的user_id，否则返回null
	 */
	public static String getUserIdFromSession(Channel session)
	{
		Object attr = null;
		if(session != null)
		{
			attr = session.attr(USER_ID_IN_SESSION_ATTRIBUTE_ATTR).get();
			if(attr != null)
				return (String)attr;
		}
		return null;
	}

	/**
	 * 指定用户ID的用户是否在线。
	 *
	 * @param userId 用户名ID（就是用户登陆名）
	 * @return true表示该用户在线，否则不在线
	 * @since 3.0
	 */
	public static boolean isOnline(String userId)
	{
		return OnlineProcessor.getInstance().getOnlineSession(userId) != null;
	}
}
